home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / snz128s / src / unbatch.c < prev    next >
C/C++ Source or Header  |  1994-06-18  |  40KB  |  1,376 lines

  1. /*
  2.     SNEWS 2.00
  3.  
  4.     unbatch - quick and dirty news toss, no feeding of other sites
  5.  
  6.  
  7.     Copyright (C) 1991  John McCombs, Christchurch, NEW ZEALAND
  8.                         john@ahuriri.gen.nz
  9.                         PO Box 2708, Christchurch, NEW ZEALAND
  10.  
  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License, version 1, as
  13.     published by the Free Software Foundation.
  14.  
  15.     This program is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU General Public License for more details.
  19.  
  20.     See the file COPYING, which contains a copy of the GNU General
  21.     Public License.
  22.  
  23.  
  24.     USAGE: unbatch {[-a] [-n] [-v] [-i] [-j] [-f] [-l] [-s] [-d]}
  25.         -a  means add any news groups found
  26.         -n  means decompress the first batch, then stop before tossing
  27.         -v  means verbose, tell what we are doing
  28.         -i  means query for adding any new groups found
  29.         -j  means junk, don't add to junk if posted elsewhere
  30.         -f  means filter, filter out non printable characters
  31.         -l  means ignore length in headers
  32.         -s  means don't check for free space
  33.         -x  means run Snews on successful end
  34.         -d  means force unbatch to process on disk
  35.  
  36.     NOTE: for the Atari version the meaning of the -f and -l switches
  37.           have been reversed.
  38.  
  39.           -f means do not filter out non-printable characters
  40.           -l means do not ignore lengths in headers
  41.  
  42.           I have done this because the previous action was causing
  43.           problems (bus errors) and I do not have time to investigate
  44.           why.
  45.  
  46.         
  47. */
  48. /*---------------------------- Source Control ------------------------------*/
  49.  
  50. /*
  51.  * $Id: UNBATCH.C,v 1.2 1994/02/05 18:50:32 gbj Exp user $
  52.  */
  53.  
  54.  
  55. /****************************************************************************
  56. *   20 May 92   1.2     GT  ka9q mods.                                      *
  57. *   08 Jun 92   1.3     GT  FQDN in "Path:".                                *
  58. *                           Fix disc space check.                           *
  59. *   09 Jun 92   1.4     GT  Only expand "Path:" in header.                  *
  60. *   12 Jun 92   1.5    NJL  Check for duplicate articles; add verbose mode; *
  61. *                           restructure toss(); improve performance;        *
  62. *                           slightly improve #!rnews test.                  *
  63. *   24 Jun 92   1.6     GT  Ensure that spool_file is open in toss ().      *
  64. *   17 Jul 92   1.7     GT  C++ compilation.                                *
  65. *   27 Jul 92   1.8    MSM  Add -j option, only post to junk if not posted  *
  66. *                           elsewhere.                                      *
  67. *                           Change file I/O to binary, to allow for ^Z.     *
  68. *                           Add -f option to filter non printable chars.    *
  69. *  16 Aug 92    1.9    MSM  Snews 1.9                                       *
  70. *                           Add article count to verbose display            *
  71. *                           Check / Process lock files                      *
  72. *                           rfc1036 style header accepted, but not used     *
  73. *                           use tmp/temp environemnt variable for spool     *
  74. *                           file.                                           *
  75. *  27 Aug 92    1.10   MSM  Fix long newsgroup header line breaking unbatch *
  76. *                           Fix disk free check to check the drive newsbase *
  77. *                           the newsbase resides on, not the default drive. *
  78. *  26 Sep 92    1.11   MSM  Detect and place in control group any article   *
  79. *                           with a control header.                          *
  80. *  27 Nov 92    1.12   MSM  Article Length display, clean up lock file on   *
  81. *                           exits.                                          *
  82. *  29 Dec 92    1.13   MSM  Save startup environment.                       *
  83. *                           Check article length when available but add -l  *
  84. *                           flag to disable checks.                         *
  85. *                           Add new groups found in newsbatches to newsbase *
  86. *                           Clean up exit when history.snw not found        *
  87. *                           Change option messages                          *
  88. *                           Make checking of free space optional            *
  89. *  1 Feb 93    1.14    MSM  Change code to access temporary files           *
  90. * 22 Feb 93    1.15    MSM  Wrong verbose article length fixed              *
  91. * 31 May 93    1.16    MSM  Snews 2.0                                       *
  92. *                           Fix prescan for new groups picking up from body *
  93. *                           In memory article extraction                    *
  94. *                           Tidy up                                         *
  95. *                           Large article processing to disk                *
  96. * 10 Jul 93    1.17    MSM  Correct assumption about memory overhead        *
  97. *                           Read only first Newsgroups: line                *
  98. *                           (as requested by G.Toal)                        *
  99. *  1 Sep 93    1.18    MSM  Add use disk option and history full warning    *
  100. *  5 Sep 93    1.19    MSM  Atexit added                                    *
  101. * 10 Oct 93    1.20    MSM  Truncate long (illegal) subject lines           *
  102. * 18 oct 93    1.21    MSM  Long integer handling tidied up                 *
  103. *                           Return codes added                              *
  104. *                           Load failure due to size of history only        *
  105. *                           reported if verbose mode is on.                 *
  106. *  5 Dec 93    1.22    MSM  Subject limited to 128 characters               *
  107. *  9 Dec 93    1.23    MSM  Error reporting code added                *
  108. *  2 Apr 94    1.24    MSM  Delete all lock files on error exits        *
  109. *                  Account for use of memory by history list when  *
  110. *                unbatching articles                    *
  111. * 11 Jun 94    AT      GBJ  Reverse meaning of -l -f switches               *
  112. ****************************************************************************/
  113.  
  114. #include <ctype.h>
  115. #include <fcntl.h>
  116. #include <io.h>
  117. #include <process.h>
  118. #ifdef __TURBOC__
  119. #    include <dir.h>
  120. #else
  121. #    ifdef ATARI
  122. #        include <sys/dir.h>
  123. #        include <stdlib.h>
  124. #        include <ext.h>
  125. #        include <errno.h>
  126. #    else
  127. #        include <direct.h>
  128. #    endif
  129. #endif
  130. #include "defs.h"
  131. #include "unbatch.h"
  132. #include "locking.h"
  133.  
  134. #ifndef __TURBOC__
  135. #ifndef ATARI
  136. unsigned long farcoreleft(void);
  137. unsigned long testcoreleft(void);
  138. #define ffblk _find_t
  139. #define ff_reserved reserved
  140. #define ff_attrib attrib
  141. #define ff_ftime wr_time
  142. #define ff_date wr_date
  143. #define ff_size size
  144. #define ff_name name
  145. #define findfirst(a, b, c) _dos_findfirst(a, c, b)
  146. #define findnext _dos_findnext
  147. #define getdfree _dos_getdiskfree
  148. #define df_avail avail_clusters
  149. #define df_total total_clusters
  150. #define df_bsec bytes_per_sector
  151. #define df_sclus sectors_per_cluster
  152. #endif
  153. #endif
  154.  
  155. #ifndef ATARI
  156. int             getopt(int argc, char **argv, char *opts);
  157. #endif
  158.  
  159. #ifdef ATARI
  160. extern unsigned long _STACK = 32768;
  161. int statx(const char *, struct stat *);
  162. #else
  163. unsigned        _stklen = 16384;
  164. #endif
  165.  
  166. INFO            my_stuff;
  167.  
  168. /* There are far too many statics here, it needs sorting out ! */
  169.  
  170. static char     buf[512], msg[256], subject[256], msg_id[256];
  171. static char     newsgroups[512];
  172. static time_t   t;
  173.  
  174. #ifdef ATARI
  175. static char    *Usage = "\
  176. Usage: unbatch [-a] [-d] [-f] [-i] [-j] [-l] [-n] [-s] [-v] [-x]\n\
  177.          a = add automatically any new groups found in news batch\n\
  178.          d = process via disk\n\
  179.          f = do not filter out non printable characters\n\
  180.          i = add interactivly any new groups found in news batch\n\
  181.          j = don't add to junk if posted elsewhere\n\
  182.          l = check article length\n\
  183.          n = no unpacking to newsbase (test mode)\n\
  184.          s = do not check for free space before unbatching\n\
  185.          v = verbose mode\n\
  186.          x = execute Snews on exit\n\n";
  187. #else
  188. static char    *Usage = "\
  189. Usage: unbatch [-a] [-d] [-f] [-i] [-j] [-l] [-n] [-s] [-v] [-x]\n\
  190.          a = add automatically any new groups found in news batch\n\
  191.          d = process via disk\n\
  192.          f = filter out non printable characters\n\
  193.          i = add interactivly any new groups found in news batch\n\
  194.          j = don't add to junk if posted elsewhere\n\
  195.          l = don't check article length\n\
  196.          n = no unpacking to newsbase (test mode)\n\
  197.          s = do not check for free space before unbatching\n\
  198.          v = verbose mode\n\
  199.          x = execute Snews on exit\n\n";
  200. #endif
  201.  
  202. static char     verbose = 0;
  203.  
  204. static char     cixmode = 0;
  205.  
  206. static char     CrossPostToJunk = 1;
  207.  
  208. static char     batch_name[65] = {""};
  209.  
  210. static char     spool_name[65];
  211.  
  212. static unsigned int article_count = 0;
  213.  
  214. static unsigned int duplicate_count = 0;
  215.  
  216. static unsigned int junk_count = 0;
  217.  
  218. #ifdef ATARI
  219. static unsigned int filter_mode = 1;
  220. #else
  221. static unsigned int filter_mode = 0;
  222. #endif
  223.  
  224. #ifdef ATARI
  225. static unsigned int no_length = 1;
  226. #else
  227. static unsigned int no_length = 0;
  228. #endif
  229.  
  230. static unsigned int control_flag = 0;
  231.  
  232. static unsigned long art_length = 0l;
  233.  
  234. static unsigned long prev_art_length = 0l;
  235.  
  236. static unsigned long art_start = 0l;
  237.  
  238. static unsigned long art_end = 0l;
  239.  
  240. static unsigned long art_end_calc = 0l;
  241.  
  242. static unsigned long art_lines = 0l;
  243.  
  244. static unsigned int interactive_add = 0;
  245.  
  246. static unsigned int add_all = 0;
  247.  
  248. static unsigned int space_mode = 0;
  249.  
  250. static unsigned int Exec_Snews = 0;
  251.  
  252. static unsigned long Free_Mem;
  253.  
  254. static unsigned int disk_cnt = 0;
  255.  
  256. static unsigned long MinFree = 0l;
  257.  
  258. static unsigned long MaxFree = 0l;
  259.  
  260. static unsigned long LargestArt = 0l;
  261.  
  262. static int Via_Disk = 0;
  263.  
  264. #ifdef ATARI
  265. extern char **environ;        /* the environment */
  266. #endif
  267.  
  268. /*------------------------------- main --------------------------------*/
  269. int             main(int argc, char *argv[])
  270. {
  271.     FILE           *tmp_file, *spool_file;
  272.     static char     name[256], in_name[256];
  273.     int             ans, done, no_toss = 0;
  274. #ifndef ATARI
  275.     int             drive;
  276. #else
  277.     dev_t drive;
  278.     char drive_letter[]="?ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  279.     long disk_total;
  280. #endif
  281.     long            required_disk, disk_free;
  282.     struct stat     st;
  283. #ifdef __TURBOC__
  284.     struct dfree    df;
  285. #else
  286. #ifdef ATARI
  287.     struct dfree    df;
  288. #else
  289.     struct _diskfree_t df;
  290. #endif
  291. #endif
  292.     struct ffblk    ffblk;
  293.     int             c;
  294.     int             ret_code;
  295.  
  296.     if (fprintf(stderr, "Demon Internet Simple News v%d.%02d [build %d]\n",
  297.         rmj, rmm, rup) == EOF)
  298.         abort_error(1, "");
  299.     fprintf(stderr, "Unbatch New News\n\n");
  300.  
  301.     atexit(end_stats);
  302.     Free_Mem = farcoreleft();
  303.     MinFree = MaxFree = Free_Mem;
  304.  
  305.     while ((c = getopt(argc, argv, "aAdDfFiIjJlLsSnNvVxX?")) != EOF)
  306.         switch (tolower(c)) {
  307.           case 'a':
  308.             /* add mode */
  309.             add_all = 1;
  310.             interactive_add = 0;
  311.             fprintf(stderr, "\tAll new Newsgroups will be added.\n");
  312.             break;
  313.           case 'd':
  314.             /* disk mode */
  315.             Via_Disk = 1;
  316.             fprintf(stderr, "\tProcessing will be forced via disk.\n");
  317.             break;
  318.           case 'f':
  319.             /* Filter mode */
  320. #ifdef ATARI
  321.             filter_mode = 0;
  322.             fprintf(stderr, "\tNon printing characters will not be removed.\n");
  323. #else
  324.             filter_mode = 1;
  325.             fprintf(stderr, "\tNon printing characters will be removed.\n");
  326. #endif            
  327.             break;
  328.           case 'i':
  329.             /* Interactive add new groups */
  330.             interactive_add = 1;
  331.             add_all = 0;
  332.             fprintf(stderr, "\tNew Newsgroups will be added interactively.\n");
  333.             break;
  334.           case 'l':
  335.             /* if TRUE then don't test article lengths */
  336. #ifdef ATARI
  337.             no_length = 0;
  338.             fprintf(stderr, "\tArticle length will be checked.\n");
  339. #else
  340.             no_length = 1;
  341.             fprintf(stderr, "\tArticle length will not be checked.\n");
  342. #endif
  343.             break;
  344.           case 'n':
  345.             /* if TRUE, then just uncompress, don't unbatch */
  346.             no_toss = 1;
  347.             fprintf(stderr, "\tUncompress pass only will be run.\n");
  348.             break;
  349.           case 's':
  350.             /* free space mode */
  351.             space_mode = 1;
  352.             fprintf(stderr, "\tFree space will not be checked.\n");
  353.             break;
  354.           case 'v':
  355.             /* Verbose mode */
  356.             verbose = 1;
  357.             fprintf(stderr, "\tProgress will be reported verbosely.\n");
  358.             break;
  359.           case 'c':
  360.             /* cix mode - no #!rnews separators (not yet implemented) */
  361.             cixmode = 1;
  362.             fprintf(stderr, "\tCIX mode is set on.\n");
  363.             break;
  364.           case 'j':
  365.             /* Junk Mode - crosspost to junk supressed */
  366.             CrossPostToJunk = 0;
  367.             fprintf(stderr, "\tArticles will not be crossposted to Junk.\n");
  368.             break;
  369.           case 'x':
  370.             /* Execute Snews on exit */
  371.             Exec_Snews = 1;
  372.             fprintf(stderr, "\tSnews will be run on successful completion.\n");
  373.             break;
  374.           case '?':
  375.             fprintf(stderr, Usage);
  376.             exit(1);
  377.         }
  378.  
  379.     if (!load_stuff()) {
  380.         fprintf(stderr, "unbatch: Couldn't read rc info\n");
  381.         exit(1);
  382.     }
  383.     sprintf(in_name, "%s*.lck", my_stuff.incoming_dir);
  384.     done = findfirst(in_name, &ffblk, 0);
  385.     if (!done) {
  386.         fprintf(stderr, "unbatch: Article file(s) locked!\n");
  387.         exit(1);
  388.     }
  389.     sprintf(in_name, "%shistory.lck", my_stuff.news_dir);
  390.     done = findfirst(in_name, &ffblk, 0);
  391.     if (!done) {
  392.         fprintf(stderr, "unbatch: History file locked!\n");
  393.         exit(1);
  394.     }
  395.     sprintf(in_name, "%s*.*", my_stuff.incoming_dir);
  396.     done = findfirst(in_name, &ffblk, 0);
  397.     strcpy(batch_name, ffblk.ff_name);
  398.     while (!done) {
  399.         if (mlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch") != 0) {
  400.             /* Fudge to avoid processing or complaining about .lck files */
  401.             if (strstr(ffblk.ff_name, ".LCK") == NULL)
  402.                 fprintf(stderr, "unbatch: Can't lock article file %s%s\n",
  403.                         my_stuff.incoming_dir, ffblk.ff_name);
  404.             done = findnext(&ffblk);
  405.         }
  406.         else
  407.             break;
  408.     }
  409.  
  410.     if (done) {
  411.         fprintf(stderr, "unbatch: Nothing to process!\n");
  412.         if (Exec_Snews == 1) {
  413.             fprintf(stderr, "\n\nLoading Snews ....\n");
  414. #ifdef ATARI
  415.             ans = forklpe("snews", "snews", NULL, environ);
  416. #else
  417.             ans = execlp("snews", "snews", NULL);
  418. #endif
  419.             fprintf(stderr, "unbatch: exec error %d\n", ans);
  420.         }
  421.         exit(1);
  422.     }
  423.  
  424.     if (add_all != 0)                      /* add new groups ? */
  425.         proc_new(ffblk.ff_name, 1);
  426.     if (interactive_add != 0)
  427.         proc_new(ffblk.ff_name, 2);
  428.  
  429.     if (mlock(my_stuff.news_dir, "history", "Unbatch") != 0) {
  430.         fprintf(stderr, "unbatch: Can't lock history file!\n");
  431.         rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  432.         exit(1);
  433.     }
  434.     load_active_file();
  435.  
  436.     free_ng();
  437.  
  438.     sprintf(name, "%shistory.snw", my_stuff.news_dir);
  439.     if ((tmp_file = fopen(name, "rb")) == NULL) {
  440.         fprintf(stderr, "\n\rHistory file not found, create one (y/n) ? ");
  441.         ans = getch();
  442.         putch(ans);
  443.         putch('\n');
  444.         putch('\r');
  445.         ans = tolower(ans);
  446.         if (ans != 'y') {
  447.             fprintf(stderr, "unbatch: No history file!\n");
  448.             rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  449.             rmlock(my_stuff.news_dir, "History", "Unbatch");
  450.             exit(1);
  451.         }
  452.         tmp_file = fopen(name, "wb");
  453.         if (tmp_file == NULL) {
  454.             fprintf(stderr, "unbatch: Unable to create history file!\n");
  455.             rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  456.             rmlock(my_stuff.news_dir, "History", "Unbatch");
  457.             exit(1);
  458.         }
  459.     }
  460.     if (fclose(tmp_file) == EOF)
  461.         abort_error(2, "Closing history.snw");
  462.  
  463.     load_history_list(0);
  464.  
  465.     Free_Mem = farcoreleft();
  466.     if ((Free_Mem < HIST_MEM_LIMIT) && (verbose == 1)) {
  467.         fprintf(stderr, "unbatch: Insufficient memory to load entire history\n");
  468.         fprintf(stderr, "         duplicate articles may not be detected. Proceed (y/n) ? ");
  469.         ans = getch();
  470.         ans = toupper(ans);
  471.         putch(ans);
  472.         putch('\n');
  473.         putch('\r');
  474.         if (ans != 'Y') {
  475.             rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  476.             rmlock(my_stuff.news_dir, "History", "Unbatch");
  477.             exit(1);
  478.         }
  479.     }
  480.     MinFree = Free_Mem;
  481.     MaxFree = Free_Mem;
  482.     Free_Mem = ((Free_Mem) / 3l);
  483.     if (Free_Mem < 16384l) {
  484.         fprintf(stderr, "unbatch: Insufficient memory available!\n");
  485.         rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  486.         rmlock(my_stuff.news_dir, "History", "Unbatch");
  487.         exit(1);
  488.     }
  489.  
  490.     sprintf(spool_name, "%s\\$$item", my_stuff.temp_name);
  491.     if ((spool_file = fopen(spool_name, "w+b")) == NULL) {
  492.         fprintf(stderr, "unbatch: Unable to create temporary file!\n");
  493.         rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  494.         rmlock(my_stuff.news_dir, "History", "Unbatch");
  495.         exit(1);
  496.     }
  497.  
  498.     if (fclose(spool_file) == EOF)    /* we may never use it ! */
  499.         abort_error(3, "Closing temporary spool file");
  500.     if (unlink(spool_name) != 0)
  501.         abort_error(4, "Deleting temporary spool file");
  502.     
  503.  
  504.     while (!done) {
  505.  
  506.         sprintf(name, "%s%s", my_stuff.incoming_dir, ffblk.ff_name);
  507.         fprintf(stderr, "unbatch: processing %s\n", name);
  508.  
  509.         /*
  510.          * Check for enough room.    We need:
  511.          * 
  512.          * - more than twice the size of the batch for uncompressed batch,
  513.          *   say 1.2 * space for the articles.
  514.          * 
  515.          * For example a 100k batch may require 280kb for the uncompressed
  516.          * batch, then another 280 kb for the articles.
  517.          * 
  518.          * Of course the above does not apply to non-compressed batches
  519.          * I reckon 1.2 times the size should do, allowing for slack.
  520.          */
  521.  
  522.         if (space_mode == 0) {
  523.  
  524.             if (statx(name, &st) != 0)
  525.                 abort_error(5, "Determining drive status");
  526.  
  527.             required_disk = (long)((float) st.st_size * 1.2);
  528.  
  529.             strcpy(name, getenv("SNEWS"));
  530.             if (strlen(name) > 3) {
  531.                 if (name[strlen(name)-1] == '\\')
  532.                     name[strlen(name)-1] = '\0';
  533.             }
  534.             strcat(name, "\\snews.rc");
  535.  
  536.             if(statx(name, &st) != 0)
  537.                 abort_error(6, "Determine drive status");
  538.  
  539.             drive = st.st_dev;
  540.             getdfree(drive + 1, &df);
  541.             if (df.df_sclus == 0xffff)
  542.                 abort_error(7, "Determine disk space");
  543.             disk_free = (long) df.df_avail * (long) df.df_bsec *
  544.                     (long) df.df_sclus;
  545. #ifdef ATARI
  546.             disk_total = (long)df.df_total * (long)df.df_bsec *
  547.                     (long)df.df_sclus;
  548.             printf("unbatch: Drive %c: avail %ldK, total %ldK\n", 
  549.                     drive_letter[st.st_dev+1], disk_free/1024, disk_total/1024);
  550. #endif
  551.             if (disk_free < required_disk) {
  552.                 fprintf(stderr, "unbatch: %ld bytes of disk req'd to unpack batch\n",
  553.                         required_disk);
  554.                 fprintf(stderr, "unbatch: only %ld bytes of disk space available\n",
  555.                         disk_free);
  556.                 rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  557.                 rmlock(my_stuff.news_dir, "History", "Unbatch");
  558.                 exit(1);
  559.             }
  560.         }
  561.         /* uncompress the news batch and return a pointer to the temp file */
  562.  
  563.         sprintf(name, "%s%s", my_stuff.incoming_dir, ffblk.ff_name);
  564.         
  565.         if ((tmp_file = decode_batches(name, no_toss)) != NULL) {
  566.  
  567.             open_hist_file();
  568.             toss(tmp_file);
  569.             close_hist_file();
  570.             fclose(tmp_file);
  571.  
  572.             if (unlink(name) != 0) {
  573.                 sprintf(buf, "deleting file %s", name);
  574.                 abort_error(8, buf);
  575.             }
  576.             sprintf(name, "%s\\$$item", my_stuff.temp_name);
  577.             unlink(name);
  578.  
  579.         }
  580.         else {
  581.             fprintf(stderr, "unbatch: could not unpack compressed news batch %s\n", name);
  582.             rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  583.             rmlock(my_stuff.news_dir, "History", "Unbatch");
  584.             exit(1);
  585.         }
  586.         rmlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch");
  587.         while ((done = findnext(&ffblk)) == 0) {
  588.             if (mlock(my_stuff.incoming_dir, ffblk.ff_name, "Unbatch") != 0) {
  589.                 /* Fudge to avoid processing or complaining about .lck files */
  590.                 if (strstr(ffblk.ff_name, ".LCK") == NULL)
  591.                     fprintf(stderr, "unbatch: Unable to lock article file %s%s\n",
  592.                             my_stuff.incoming_dir, ffblk.ff_name);
  593.             }
  594.             else
  595.                 break;
  596.         }
  597.  
  598.     }
  599.  
  600.     close_active_file();
  601.     free_hist_list();
  602.     rmlock(my_stuff.news_dir, "history", "Unbatch");
  603.  
  604.     if (Exec_Snews == 1) {
  605.         fprintf(stderr, "\n\nLoading Snews ....\n");
  606. #ifdef ATARI
  607.         ans = forklpe("snews", "snews", NULL, environ);
  608. #else
  609.         ans = execlp("snews", "snews", NULL);
  610. #endif
  611.         fprintf(stderr, "unbatch: exec error %d\n", ans);
  612.     }
  613.     ret_code = 0;
  614.     return ret_code;
  615. }
  616.  
  617. /*---------------------------- Error Handler -------------------------------*/
  618. void        abort_error(int numb, char *text)
  619. {
  620.     char *buffer;
  621.  
  622.     rmlock(my_stuff.news_dir, "history", "Unbatch");
  623.     rmlock(my_stuff.incoming_dir, batch_name, "Unbatch");
  624.     printf("\n\n");
  625.     printf("unbatch: A fatal error occurred, error #%d\n", numb);
  626.     printf("         %s\n", text);
  627.     printf("         errno is %d\n", errno);
  628.     buffer = strerror(errno);
  629.     printf("         %s\n\n", buffer);
  630.     printf("Unbatch did not complete successfully\n\n");
  631.     if (fcloseall() == EOF)
  632.         printf("Some files were not successfully closed\n\n");
  633.     exit(2);
  634. }
  635.  
  636. /*----------------------------- Ending Stats -------------------------------*/
  637. void               end_stats()
  638. {
  639.     fprintf(stderr, "unbatch: complete, %u articles, %u duplicates, %u via disk, %u junked\n",
  640.             article_count, duplicate_count, disk_cnt, junk_count);
  641.     fprintf(stderr, "         Initial free memory %lu, minimum free memory  %lu\n",
  642.             MaxFree, MinFree);
  643.     fprintf(stderr, "         Memory used %lu largest article was %lu\n", MaxFree-MinFree,
  644.             LargestArt);
  645. }
  646.  
  647. /*--------------------------- post article work file to all groups ----*/
  648. LINES          *post_to_groups(LINES * spool, FILE *spool_file)
  649. {
  650.     FILE           *out_file = NULL;
  651.     LINES          *help;
  652.     static char     nglist[512], junk_test[512];
  653.     long            where;
  654.     ACTIVE         *gp;
  655.     char           *p;
  656.     int             junk_flag;              /* nz - junk to junk group */
  657.     int             already_junked;          /* nz - message already junked */
  658.  
  659.     already_junked = 0;
  660.     nglist[0] = '\0';
  661.     
  662.     if ((spool == NULL) && (spool_file == NULL))
  663.         return NULL;
  664.  
  665.     if (strlen(newsgroups) != 0) {
  666.  
  667.         if (verbose) {
  668.             if (art_length > 0)
  669.                 fprintf(stderr, "%4.4u %5.5ld bytes, Message-ID: %s\n",
  670.                         article_count, prev_art_length, msg_id);
  671.             else
  672.                 fprintf(stderr, "%4.4u, Message-ID: %s\n", article_count, msg_id);
  673.         }
  674.         /* Check if we've already got this message from another feed */
  675.         /* Shouldn't happen with NNTP, but can do with UUCP or CIX   */
  676.         if (find_msg_id(msg_id) == NULL) {
  677.             if (control_flag == TRUE)
  678.                 strcpy(newsgroups, "Newsgroups: control");
  679.             strcpy(junk_test, newsgroups);
  680.  
  681.             /* If junk flag set, step through newsgroups line to see if  */
  682.             /* this article is to be posted to an active group. If so      */
  683.             /* set already_junked to prevent crossposting to junk.          */
  684.  
  685.             if (!CrossPostToJunk) {
  686.                 p = strtok(junk_test, " \t\r\n,:");
  687.                 p = strtok(NULL, " \t\r\n,:");
  688.                 while (p != '\0') {
  689.                     gp = find_news_group(p, &junk_flag);
  690.                     if (junk_flag == 0) {  /* Posting to active group */
  691.                         already_junked = 1;    /* No need to post to junk */
  692.                         break;
  693.                     }
  694.                     p = strtok(NULL, " \t\r\n,:");
  695.                 }
  696.                 strcpy(junk_test, newsgroups);
  697.                 /* if omitting a group then don't place in nglist */
  698.                 p = strtok(junk_test, " \t\r\n,:");
  699.                 p = strtok(NULL, " \t\r\n,:");
  700.                 if (already_junked == 0)
  701.                     strcpy(nglist, newsgroups + 11);
  702.                 else {
  703.                     while (p != '\0') {
  704.                         gp = find_news_group(p, &junk_flag);
  705.                         if (junk_flag == 0)
  706.                             strcat(nglist, p);
  707.                         p = strtok(NULL, " \t\r\n,:");
  708.                         if ((p != '\0') && (junk_flag == 0))
  709.                             strcat(nglist, ",");
  710.                     }
  711.                     strcat(nglist, "\n");
  712.                 }
  713.             }
  714.             else
  715.                 strcpy(nglist, newsgroups + 11);
  716.  
  717.             /* Step through the list of newsgroups */
  718.  
  719.             p = strtok(newsgroups, " \t\r\n,:");
  720.             p = strtok(NULL, " \t\r\n,:");
  721.  
  722.             /*
  723.              * For each newsgroup
  724.              * - open the text file
  725.              * - save the file pointer
  726.              * - append the message to it
  727.              * - close the file
  728.              * - open the index file
  729.              * - save the file pointer and the subject line
  730.              */
  731.  
  732.             if (verbose)
  733.                 fputc('\t', stderr);
  734.  
  735.             while (p != '\x00') {
  736.  
  737.                 out_file = open_out_file(p, &junk_flag);
  738.  
  739.                 if (verbose) {
  740.                     if (junk_flag)
  741.                         fprintf(stderr, " (%s)", p);
  742.                     else
  743.                         fprintf(stderr, " %s ", p);
  744.                 }
  745.                 if (junk_flag != 0) {
  746.                     /* Check to see if we have already posted this in "junk". */
  747.  
  748.                     if (already_junked == 0) {
  749.                         already_junked = 1;
  750.                         junk_count++;
  751.                     }
  752.                     else {
  753.                         /* Already posted in junk - skip it. */
  754.  
  755.                         if (fclose(out_file) == EOF) {    /* skip this */
  756.                             sprintf(buf, "closing %s", p);
  757.                             abort_error(10, buf);
  758.                         }
  759.                         
  760.  
  761.                         /* Reset the high message number. */
  762.  
  763.                         gp = find_news_group(p, &junk_flag);
  764.                         (gp->hi_num)--;
  765.                         update_active_entry(gp);
  766.                         p = strtok(NULL, " \t\r\n,:");    /* next group */
  767.                         continue;
  768.                     }
  769.  
  770.                 }                          /* if (junk_flag != 0) */
  771.                 
  772.                 where = ftell(out_file);
  773.                 if (where == -1l) {
  774.                     sprintf(buf, "getting pointer in %s", p);
  775.                     abort_error(11, buf);
  776.                 }
  777.                 if (spool != NULL) {
  778.                 for (help = spool; help != NULL; help = help->next)
  779.                         if (fputs(help->data, out_file) == EOF) {
  780.                             sprintf(buf, "writing to %s", p);
  781.                             abort_error(12, buf);
  782.                         }
  783.                 }
  784.                 else {
  785.                     rewind(spool_file);
  786.                     while (fgets(buf, 511, spool_file) != NULL)
  787.                         if (fputs(buf, out_file) == EOF) {
  788.                             sprintf(buf, "writing to %s", p);
  789.                             abort_error(13, buf);
  790.                         }
  791.                 }
  792.                 if (fprintf(out_file, "\n@@@@END\n") == EOF) {
  793.                     sprintf(buf, "writing to %s", p);
  794.                     abort_error(13, buf);
  795.                 }
  796.                 if (fclose(out_file) == EOF) {
  797.                     sprintf(buf, "closing file %s", p);
  798.                     abort_error(15, buf);
  799.                 }
  800.  
  801.                 out_file = open_index_file(p);
  802.                 gp = find_news_group(p, &junk_flag);
  803.                 if (fprintf(out_file, "%08ld %08ld %09ld %s", where,
  804.                         gp->hi_num, t, subject) == EOF) {
  805.                     sprintf(buf, "writing to %s", p);
  806.                     abort_error(16, buf);
  807.                 }
  808.                 if (fclose(out_file) == EOF) {
  809.                     sprintf(buf, "closing %s", p);
  810.                     abort_error(17, buf);
  811.                 }
  812.                 Free_Mem -= 3 * (sizeof(HIST_LIST)); /* account for history in memory */
  813.  
  814.                 p = strtok(NULL, " \t\r\n,:");
  815.             }
  816.             if (verbose)
  817.                 fputc('\n', stderr);
  818.             add_hist_record(msg_id, nglist);
  819.             if (spool == NULL)
  820.                 disk_cnt++;
  821.         }
  822.         else {
  823.             if (verbose)
  824.                 fprintf(stderr, "\tDuplicate article\n");
  825.             duplicate_count++;
  826.         }
  827.     }
  828.     if (MinFree > farcoreleft())
  829.         MinFree = farcoreleft();
  830.     if (spool != NULL) {
  831.         while (spool) {
  832.             help = spool;
  833.             spool = spool->next;
  834.             free(help->data);
  835.             free(help);
  836.         }
  837.     }
  838.     else {
  839.         if (fclose(spool_file) == EOF)
  840.             abort_error(18, spool_name);
  841.         if (unlink(spool_name) == -1)
  842.             abort_error(19, spool_name);
  843.         spool_file = NULL;
  844.     }
  845.     return spool;                          /* == NULL */
  846. }
  847.  
  848. /*--------------------------- unpack the batch ------------------------*/
  849. void            toss(FILE * tmp_file)
  850. {
  851.  
  852.     /*
  853.      * Toss it into the appropriate files.
  854.      * 
  855.      */
  856.  
  857.     LINES          *spool = NULL, *help;
  858.     FILE           *spool_file = NULL;
  859.     char           *p;
  860.     int /* already_junked, */ in_header;
  861.     int             token, first_token;
  862.     int             on_disk = 0;
  863.     char            buf2[512];
  864.  
  865.  
  866.     strcpy(msg_id, "");
  867.     strcpy(subject, "-- no subject --\n");
  868.     strcpy(newsgroups, "");
  869.  
  870.     rewind(tmp_file);
  871.     time(&t);
  872.     in_header = TRUE;
  873.  
  874.     /* set a large disk transfer buffer */
  875.     /* read the file */
  876.     first_token = 0;
  877.     while (fgets(buf, 511, tmp_file) != NULL) {
  878.  
  879.         if (!filter_mode)
  880.             remcr(buf);
  881.         else
  882.             filter(buf);
  883.  
  884.         /*
  885.          * Check for a line containing only the Rnews token or rfc1036 style
  886.          * token + count.
  887.          * This is still liable to abuse but not so much so as accepting that
  888.          * token *anywhere* in the line, like it used to do.
  889.          * 
  890.          * art_length is losely checked to see it the Rnews token is at the
  891.          * expected place in the file. If no the token is assumed to be part
  892.          * of an article.
  893.          * 
  894.          * The test could still be made more secure (i.e. if the count is
  895.          * there, ensure nothing else follows it).
  896.          */
  897.  
  898.         token = 0;
  899.  
  900.         prev_art_length = art_length;
  901.         if (prev_art_length > LargestArt)
  902.             LargestArt = prev_art_length;
  903.  
  904.         if (strncmp(buf, "#! rnews", 8) == 0 &&
  905.             ((strcspn(buf + 8, "\r\n") == 0) ||
  906.              ((art_length = atol(buf + 8)) != 0l)))
  907.             token = 1;                      /* Token found */
  908.  
  909.         if ((first_token == 0) && token == 1) {
  910.             art_start = ftell(tmp_file);
  911.             art_end_calc = art_start + art_length;
  912.         }
  913.         if (first_token != 0) {
  914.             if ((no_length == 0) && (token == 1) &&
  915.                 (((art_end_calc + art_lines - 1) - 10) > art_end))
  916.                 token = 0;                  /* Token not within 10 bytes of correct place */
  917.         }
  918.         else {
  919.             first_token = 1;
  920.         }
  921.         
  922.         if (token == 1) {
  923.  
  924.             art_start = ftell(tmp_file);
  925.             art_end_calc = art_start + art_length;
  926.             art_lines = 0l;
  927.             if ((art_length > Free_Mem) || (art_length == 0l) || (Via_Disk != 0))
  928.                 on_disk = 1;
  929.             else
  930.                 on_disk = 0; /* this one to 0 */
  931.             spool = post_to_groups(spool, spool_file);
  932.             spool_file = NULL;
  933.             article_count++;
  934.  
  935.             strcpy(subject, "-- no subject --\n");
  936.             strcpy(newsgroups, "");
  937.             in_header = TRUE;
  938.             control_flag = FALSE;
  939.  
  940.         }
  941.         else {
  942.             /* flag the end of the header */
  943.             if (strcmp(buf, "\n") == 0)
  944.                 in_header = FALSE;
  945.  
  946.             /* save the newsgroups line */
  947.             if (in_header) {
  948.                 if (strnicmp(buf, "Message-ID:", 11) == 0) {
  949.                     strcpy(msg, buf);
  950.                     p = strtok(msg, " \t\r\n");
  951.                     p = strtok(NULL, " \t\r\n");
  952.                     strcpy(msg_id, p);
  953.                 }
  954.                 if ((strnicmp(buf, "Newsgroups:", 11) == 0) && (newsgroups[0] == '\0'))
  955.                     strcpy(newsgroups, buf);
  956.                 if (strnicmp(buf, "Control:", 8) == 0)
  957.                     control_flag = TRUE;
  958.                 if (strnicmp(buf, "Subject:", 8) == 0) {
  959.                     if (strlen(buf) > 136) {
  960.                         buf[136] = '\n';
  961.                         buf[137] = '\0';
  962.                     }
  963.                     strcpy(subject, buf + 8);
  964.                 }
  965.                 /* add our system name to the path list */
  966.                 if (strnicmp(buf, "Path:", 5) == 0) {
  967.                     p = strtok(buf, " \t");
  968.                     p = strtok(NULL, " \t");
  969.                     sprintf(buf2, "Path: %s.%s!%s", my_stuff.my_site,
  970.                             my_stuff.my_domain, p);
  971.                     if (on_disk == 0) {
  972.                         if (spool == NULL)
  973.                             spool = help = (LINES *) malloc(sizeof(LINES));
  974.                         else {
  975.                             help->next = (LINES *) malloc(sizeof(LINES));
  976.                             help = help->next;
  977.                         }
  978.                         help->next = NULL;
  979.                         help->data = (char *) malloc(strlen(buf2) + 1);
  980.                         strcpy(help->data, buf2);
  981.                     }
  982.                     else {
  983.                         if (spool_file == NULL)
  984.                             spool_file = fopen(spool_name, "w+b");
  985.                         if (spool_file == NULL) {
  986.                             sprintf(buf, "opening to %s", spool_name);
  987.                             abort_error(20, buf);
  988.                         }
  989.                         if (fprintf(spool_file, "%s", buf2) == EOF) {
  990.                             sprintf(buf, "writing to %s", spool_name);
  991.                             abort_error(21, buf);
  992.                         }
  993.                     }
  994.                 }
  995.                 else {
  996.                     if (on_disk == 0) {
  997.                         if (spool == NULL)
  998.                             spool = help = (LINES *) malloc(sizeof(LINES));
  999.                         else {
  1000.                             help->next = (LINES *) malloc(sizeof(LINES));
  1001.                             help = help->next;
  1002.                         }
  1003.                         help->next = NULL;
  1004.                         help->data = (char *) malloc(strlen(buf) + 1);
  1005.                         strcpy(help->data, buf);
  1006.                     }
  1007.                     else {
  1008.                         if (spool_file == NULL)
  1009.                             spool_file = fopen(spool_name, "w+b");
  1010.                         if (spool_file == NULL) {
  1011.                             sprintf(buf, "opening file %s", spool_name);
  1012.                             abort_error(22, buf);
  1013.                         }
  1014.                         if (fprintf(spool_file, "%s", buf) == EOF) {
  1015.                             sprintf(buf, "writing to %s", spool_name);
  1016.                             abort_error(122, buf);
  1017.                         }
  1018.                     }
  1019.                 }
  1020.             }
  1021.             /* if (in_header) */
  1022.             else {
  1023.                 if (on_disk == 0) {
  1024.                     if (spool == NULL)
  1025.                         spool = help = (LINES *) malloc(sizeof(LINES));
  1026.                     else {
  1027.                         help->next = (LINES *) malloc(sizeof(LINES));
  1028.                         help = help->next;
  1029.                     }
  1030.                     help->next = NULL;
  1031.                     help->data = (char *) malloc(strlen(buf) + 1);
  1032.                     strcpy(help->data, buf);
  1033.  
  1034.                 }
  1035.                 else {
  1036.                     if (spool_file == NULL)
  1037.                         spool_file = fopen(spool_name, "w+b");
  1038.                     if (spool_file == NULL) {
  1039.                         sprintf(buf, "opening file %s", spool_name);
  1040.                         abort_error(23, buf);
  1041.                     }
  1042.                     if (fprintf(spool_file, "%s", buf) == EOF) {
  1043.                         sprintf(buf, "writing to %s", spool_name);
  1044.                         abort_error(24, buf);
  1045.                     }
  1046.                 }
  1047.             }
  1048.         }
  1049.         art_lines++;
  1050.         art_end = ftell(tmp_file);
  1051.         if (art_end == -1l)
  1052.             abort_error(25, "seeking in temporary file");
  1053.     }
  1054.  
  1055.     /* process the last one */
  1056.  
  1057.     spool = post_to_groups(spool, spool_file);
  1058.  
  1059. }
  1060.  
  1061. /*--------------------------- unpack the batch ------------------------*/
  1062. FILE           *decode_batches(char *fn, int no_toss)
  1063. {
  1064.  
  1065.     /*
  1066.      * take the batch, strip off the !cunbatch, and feed the file to uncompress,
  1067.      * the opened uncompressed file is returned
  1068.      */
  1069.  
  1070.     FILE           *out_file;
  1071.     size_t          bufsize;
  1072.  
  1073.     if (no_toss)
  1074.         return (NULL);
  1075.  
  1076.     /* open the uncompressed */
  1077.  
  1078.     if ((out_file = fopen(fn, "rb")) == NULL) {
  1079.         (void) printf("unbatch: cannot open uncompressed file %s\n", fn);
  1080.         return (NULL);
  1081.     }
  1082.     /* set a large disk transfer buffer */
  1083.     for (bufsize = (size_t) 32768U;
  1084.          setvbuf(out_file, NULL, _IOFBF, bufsize) != 0 && bufsize > 512;
  1085.          bufsize /= 2)
  1086.          /* do nothing */ ;
  1087.  
  1088.     return (out_file);
  1089. }                                          /* FILE *decode_batches (char *fn, int no_toss) */
  1090.  
  1091.  
  1092. /*------------------------- remove CR's from buffer ------------------------*/
  1093. void            remcr(char *buf)
  1094. {
  1095.     char           *s, *d;
  1096.  
  1097.     s = d = buf;
  1098.  
  1099.     do {
  1100.         if (*s != 0x0d)
  1101.             *d++ = *s;
  1102.     }
  1103.     while (*s++ != '\0');
  1104. }
  1105.  
  1106. /*------------------- remove non printables from buffer --------------------*/
  1107. void            filter(char *buf)
  1108. {
  1109.     char           *s, *d;
  1110.  
  1111.     s = d = buf;
  1112.  
  1113.     do {
  1114.         if (*s != 0) {
  1115.             if (*s < 0x20)                  /* Since BC is signed char will get >0x80 as well */
  1116.                 if ((*s != '\t') && (*s != '\n') && (*s != '\f') && (*s != '\r'))
  1117.                     *s = '?';
  1118.         }
  1119.         if (*s != 0x0d)
  1120.             *d++ = *s;
  1121.     }
  1122.     while (*s++ != '\0');
  1123. }
  1124.  
  1125. /*------------------- Process any new groups in batch ----------------------*/
  1126.  
  1127. static NEW_GROUP *added, *added_first;
  1128.  
  1129. void            proc_new(char *f, int t)
  1130. {
  1131.  
  1132.     FILE           *batch_file;
  1133.     int             junk_flag, already_junked, count, in_header;
  1134.     char            batch_name[80];
  1135.     char            buf[512], junk_test[512], nglist[512], *p;
  1136.     size_t          bufsize;
  1137.     ACTIVE         *gp;
  1138.  
  1139.     fprintf(stderr, "\nChecking %s for new newsgroups...", f);
  1140.     if (t == 1)
  1141.         fprintf(stderr, "\nAny found will be added to the newsbase\n\n");
  1142.     if (t == 2)
  1143.         fprintf(stderr, "\nYou will be asked if you wish to add any found\n\n");
  1144.  
  1145.     load_active_file();
  1146.  
  1147.     free_ng();
  1148.     strcpy(batch_name, my_stuff.incoming_dir);
  1149.     strcat(batch_name, f);
  1150.     if ((batch_file = fopen(batch_name, "r")) == NULL) {
  1151.         fprintf(stderr, "Unable to open batch file %s for processing\n", f);
  1152.         rmlock(my_stuff.incoming_dir, batch_name, "Unbatch");
  1153.         exit(1);
  1154.     }
  1155.     for (bufsize = (size_t) 32768U;
  1156.          setvbuf(batch_file, NULL, _IOFBF, bufsize) != 0 && bufsize > 512;
  1157.          bufsize /= 2)
  1158.          /* do nothing */ ;
  1159.  
  1160.     added_first = NULL;
  1161.     count = 0;
  1162.     in_header = 0;
  1163.  
  1164.     while (fgets(buf, 511, batch_file) != NULL) {
  1165.         if (strnicmp(buf, "#! rnews", 8) == 0)
  1166.             in_header = 1;
  1167.         if (strcmp(buf, "\n") == 0)
  1168.             in_header = 0;
  1169.         if ((strnicmp(buf, "Newsgroups:", 11) == 0) && (in_header == 1)) {
  1170.             if (verbose)
  1171.                 fprintf(stderr, "\rArticle %d", ++count);
  1172.             already_junked = 0;
  1173.             nglist[0] = '\0';
  1174.             strcpy(junk_test, buf + 11);
  1175.  
  1176.             if (!CrossPostToJunk) {
  1177.                 p = strtok(junk_test, " \t\n\r,:");
  1178.                 while (p != '\0') {
  1179.                     gp = find_news_group(p, &junk_flag);
  1180.                     if (junk_flag == 0) {  /* posting to active group */
  1181.                         already_junked = 1;
  1182.                         break;
  1183.                     }
  1184.                     p = strtok(NULL, " \t\n\r,:");
  1185.                 }
  1186.             }
  1187.             strcpy(junk_test, buf + 11);
  1188.             p = strtok(junk_test, " \t\n\r,:");
  1189.             if (already_junked == 1) {
  1190.                 nglist[0] = '\0';
  1191.             }
  1192.             else {
  1193.                 while (p != '\0') {
  1194.                     gp = find_news_group(p, &junk_flag);
  1195.                     if (junk_flag != 0)
  1196.                         strcat(nglist, p);
  1197.                     p = strtok(NULL, " \t\n\r,:");
  1198.                     if ((p != '\0') && (junk_flag != 0))
  1199.                         strcat(nglist, ",");
  1200.                 }
  1201.             }
  1202.  
  1203.             p = strtok(nglist, " \t\r\n,:");
  1204.             while (p != '\0') {
  1205.                 /* we now have a new newsgroup in p */
  1206.                 addnew(p);
  1207.                 p = strtok(NULL, " \t\n\r,:");
  1208.             }
  1209.         }
  1210.     }
  1211.     gp = gp;                              /* suppress BCC error message */
  1212.     fclose(batch_file);
  1213.     close_active_file();
  1214.  
  1215.     putch('\n');
  1216.     putch('\r');
  1217.     listnew(t);
  1218.     addem();
  1219.     freeadd();
  1220. }
  1221.  
  1222. int             addnew(char *p)
  1223. {
  1224.     if (added_first != NULL) {
  1225.         added = added_first;
  1226.         if (stricmp(added->name, p) == 0)
  1227.             return 0;                      /* In first place in table */
  1228.  
  1229.         if (added->next != NULL) {
  1230.             do {
  1231.                 added = added->next;
  1232.                 if (stricmp(added->name, p) == 0)
  1233.                     return 0;              /* Already in table */
  1234.             }
  1235.             while (added->next != NULL);
  1236.         }
  1237.     }
  1238.     /* if here p not in table */
  1239.  
  1240.     if (added_first == NULL) {
  1241.         added_first = (NEW_GROUP *) malloc(sizeof(NEW_GROUP));
  1242.         if (added_first == NULL) {
  1243.             fprintf(stderr, "unbatch: Out of memory.\n");
  1244.             rmlock(my_stuff.news_dir, "history", "Unbatch");
  1245.             rmlock(my_stuff.incoming_dir, batch_name, "Unbatch");
  1246.             exit(1);
  1247.         }
  1248.         added_first->next = NULL;
  1249.         added = added_first;
  1250.     }
  1251.     else {
  1252.         while (added->next != NULL)
  1253.             added = added->next;
  1254.         added->next = (NEW_GROUP *) malloc(sizeof(NEW_GROUP));
  1255.         if (added->next == NULL) {
  1256.             fprintf(stderr, "unbatch: out of memory.\n");
  1257.             rmlock(my_stuff.news_dir, "history", "Unbatch");
  1258.             rmlock(my_stuff.incoming_dir, batch_name, "Unbatch");
  1259.             exit(1);
  1260.         }
  1261.         added = added->next;
  1262.     }
  1263.  
  1264.     added->next = NULL;
  1265.     strcpy(added->name, p);
  1266.     added->answer = 0;
  1267.  
  1268.     return 1;                              /* added */
  1269. }
  1270.  
  1271. void            listnew(int t)
  1272. {
  1273.  
  1274.     char            ch;
  1275.  
  1276.     added = added_first;
  1277.     if (added == NULL)
  1278.         return;                              /* none to list */
  1279.  
  1280.     do {
  1281.         fprintf(stderr, "\nNew Group \"%s\" ", added->name);
  1282.         if (t == 2) {
  1283.             fprintf(stderr, "Add (Y/N) ? ");
  1284.             ch = getch();
  1285.             putch(ch);
  1286.             ch = tolower(ch);
  1287.             if (ch != 'y')
  1288.                 added->answer = 1;
  1289.         }
  1290.         added = added->next;
  1291.     }
  1292.     while (added != NULL);
  1293.  
  1294.     putch('\n');
  1295.     putch('\r');
  1296.  
  1297. }
  1298.  
  1299. void            addem(void)
  1300. {
  1301.     int             ret;
  1302.  
  1303.     added = added_first;
  1304.     if (added == NULL)
  1305.         return;
  1306.  
  1307.     do {
  1308.         if (added->answer == 0) {
  1309. #ifdef ATARI
  1310.             ret = spawnlpe(P_WAIT, "addgroup", "addgroup", added->name, NULL, environ);
  1311. #else
  1312.             ret = spawnlp(P_WAIT, "addgroup.exe", "addgroup.exe", added->name, NULL);
  1313. #endif
  1314.             if (ret == -1) {
  1315.                 fprintf(stderr, "\nError running \"addgroup %s\"\n", added->name);
  1316.             }
  1317.         }
  1318.         added = added->next;
  1319.     }
  1320.     while (added != NULL);
  1321.     putch('\n');
  1322.     putch('\r');
  1323.  
  1324.  
  1325. }
  1326.  
  1327.  
  1328. void            freeadd()
  1329. {
  1330.  
  1331.     NEW_GROUP      *next;
  1332.  
  1333.     added = added_first;
  1334.     if (added == NULL)
  1335.         return;
  1336.  
  1337.     do {
  1338.         next = added->next;
  1339.         free(added);
  1340.         if (next != NULL) {
  1341.             added = next;
  1342.         }
  1343.     }
  1344.     while (next != NULL);
  1345. }
  1346.  
  1347. #ifndef __TURBOC__
  1348. #ifndef ATARI
  1349. unsigned long farcoreleft(void)
  1350. {
  1351.  
  1352.     union _REGS inregs, outregs;
  1353.  
  1354.     _heapmin();
  1355.     inregs.h.ah = 0x48;
  1356.     inregs.x.bx = 0xffff;
  1357.     _intdos(&inregs, &outregs);
  1358.  
  1359.     return (16l * (long)outregs.x.bx);
  1360. }
  1361.  
  1362. unsigned long testcoreleft(void)
  1363. {
  1364.  
  1365.     union _REGS inregs, outregs;
  1366.  
  1367.     inregs.h.ah = 0x48;
  1368.     inregs.x.bx = 0xffff;
  1369.     _intdos(&inregs, &outregs);
  1370.  
  1371.     return (16l * (long)outregs.x.bx);
  1372. }
  1373.  
  1374. #endif    
  1375. #endif
  1376.